/*
 * @Author: 肖思汗
 * @Date: 2019-08-26 20:11:29
 * @Last Modified by: xiaosihan
 * @Last Modified time: 2021-11-07 18:36:43
 * 控制台调试工具
 */
import _ from "lodash";
import * as THREE from "three";
import { InstancedBufferAttribute, MathUtils, Scene, WebGLRenderer } from "three";
import { THREEInstancedMeshItem } from "../../InstancedMesh/THREEInstancedMesh";
import Object3D from "../../Object3D/Object3D";
import reactDevtoolPNG from "./react-devtool.png";

class ReactThreeDevTool {

    constructor() {

        this.devRenderer.setSize(1024, 1024);

        this.devRenderer.autoClear = false;

        this.devMesh.onBeforeRender = (renderer: WebGLRenderer, scene: Scene, camera: THREE.Camera) => {

            this.devRenderer.clear();

            let { width, height } = renderer.domElement;

            if (this.devObjet3d) {

                const rendererSize = new THREE.Vector2();

                this.devRenderer.getSize(rendererSize);

                if (!_.isEqual({ ...rendererSize }, { x: width, y: height })) {
                    this.devRenderer.setSize(width, height);
                }

                this.devMesh.material.uniforms.resolution.value.set(1 / width, 1 / height);

                this.devMesh.material.uniforms.offset.value.add({ x: -0.01, y: 0.01 });
                this.devScene.children = [this.devObjet3d];

                this.devRenderer.render(this.devScene, camera);

                // 处理实例模型子元素的绘制
                this.devObjet3d.traverseVisible(object => {
                    if (_.get(object, "isTHREEInstancedMeshItem")) {
                        this.drawTHREEInstancedMeshItem(object as THREEInstancedMeshItem, camera);
                    }
                });
            }

            this.devMesh.material.uniforms.map.value.needsUpdate = true;
        }

        if (ReactThreeDevTool.isInit) {
            ReactThreeDevTool.isInit = false;

            // 监听调试工具的消息
            window.addEventListener("message", this.listenerMessage);
        }
    }

    static isInit = true;

    //零时的实例模型对象
    static instanced_mesh_temp = (() => {
        const instanced_mesh = new THREE.InstancedMesh(new THREE.BufferGeometry(), new THREE.Material, 1);
        instanced_mesh.instanceColor = new InstancedBufferAttribute(new Float32Array(3), 3);
        instanced_mesh.matrixAutoUpdate = false;
        return instanced_mesh;
    })();

    // 绘制实例模型子元素
    drawTHREEInstancedMeshItem(Objet3d: THREEInstancedMeshItem, camera: THREE.Camera) {
        if (Objet3d.instancedMeshParent) {
            const { geometry, matrixWorld, material, renderOrder } = Objet3d.instancedMeshParent
            ReactThreeDevTool.instanced_mesh_temp.geometry = geometry;
            ReactThreeDevTool.instanced_mesh_temp.material = material as THREE.Material;
            ReactThreeDevTool.instanced_mesh_temp.matrixWorld.copy(matrixWorld);
            ReactThreeDevTool.instanced_mesh_temp.renderOrder = renderOrder;
            ReactThreeDevTool.instanced_mesh_temp.setMatrixAt(0, Objet3d.matrixWorld);
            ReactThreeDevTool.instanced_mesh_temp.instanceMatrix.needsUpdate = true;
            this.devScene.children = [ReactThreeDevTool.instanced_mesh_temp];
        }
        this.devRenderer.render(this.devScene, camera);
    }


    uid = MathUtils.generateUUID();

    listenerMessage = (e: any) => {

        if (_.get(e, ["data", "source"]) === "react-devtools-content-script") {

            let event = _.get(e, ["data", "payload", "event"]);
            let payload = _.get(e, ["data", "payload", "payload"]);

            switch (event) {
                case "highlightNativeElement": // 高亮组件

                    let reactInstance = this.getReactInstance(payload);

                    if (reactInstance && !_.isEmpty(reactInstance.object3d)) {
                        this.devObjet3d = reactInstance.object3d;
                    } else {
                        this.devObjet3d = undefined;
                    }
                    break;
                case "clearNativeElementHighlight": // 清除高亮
                    this.devObjet3d = undefined;
                    break;
                case "inspectElement": // 重复选中当前元素
                default:
                    break;
            }
        }
    }

    // 获取react 实例
    getReactInstance({ rendererID, id }: any) {
        let react_dev = _.get(window, ["__REACT_DEVTOOLS_GLOBAL_HOOK__"]);

        if (react_dev) {
            // 根渲染器
            let renderer = (react_dev as any).rendererInterfaces.get(rendererID);
            // 寻找react 实例
            let instanceAndStyle = renderer.getInstanceAndStyle(id);

            if (_.get(instanceAndStyle, ["instance", "object3d"])) {
                return instanceAndStyle.instance;
            } else {

                instanceAndStyle = renderer.getInstanceAndStyle(id + 1);

                return instanceAndStyle.instance;
            }
        }

        return undefined;
    }

    devObjet3d?: Object3D; // 调试的对象

    // 创建一个渲染器用于 离屏渲染
    devRenderer = new THREE.WebGLRenderer({
        alpha: true,
        antialias: true, // 阴影抗锯齿
        // localClippingEnabled: true, //裁剪功能
        logarithmicDepthBuffer: true, // 对数深度缓冲区 避免当2个面完全重合时闪烁
        preserveDrawingBuffer: false, // 深度缓冲
        stencil: false // 模版
    });

    // 创建一个可以调试的场景
    devScene = new THREE.Scene();

    // 创建六边形背景纹理
    backgroundTexture = (() => {
        let texture = new THREE.TextureLoader().load(reactDevtoolPNG);
        Object.assign(texture, { wrapS: 1000, wrapT: 1000 });
        return texture;
    })();

    // 用于绘制发光纹理的模型
    devMesh = new THREE.Mesh(
        new THREE.PlaneBufferGeometry(2, 2, 1, 1),
        new THREE.ShaderMaterial({
            uniforms: {
                map: { value: new THREE.Texture(this.devRenderer.domElement) },
                background: { value: this.backgroundTexture },
                resolution: { value: new THREE.Vector2(2048, 2048) },
                offset: { value: new THREE.Vector2(0, 0) }
            },
            vertexShader: `
                varying vec2 vUv;
                // projectionMatrix;
                // modelViewMatrix;
                void main() {
                    vUv = uv;
                    gl_Position = vec4(position, 1.0 );
                }
            `,
            fragmentShader: `
                uniform sampler2D map;
                uniform sampler2D background;
                uniform vec2 resolution;
                uniform vec2 offset;
                varying vec2 vUv;
                
                float distance(float w) {
                    float a = 0.0;
                    a = max(a, texture2D(map, vUv + (vec2(0.000, 1.000) * resolution * w)).a);
                    a = max(a, texture2D(map, vUv + (vec2(0.259, 0.966) * resolution * w)).a);
                    a = max(a, texture2D(map, vUv + (vec2(0.500, 0.866) * resolution * w)).a);
                    a = max(a, texture2D(map, vUv + (vec2(0.707, 0.707) * resolution * w)).a);
                    a = max(a, texture2D(map, vUv + (vec2(0.866, 0.500) * resolution * w)).a);
                    a = max(a, texture2D(map, vUv + (vec2(0.966, 0.259) * resolution * w)).a);
                    a = max(a, texture2D(map, vUv + (vec2(1.000, 0.000) * resolution * w)).a);
                    a = max(a, texture2D(map, vUv + (vec2(0.966, -0.259) * resolution * w)).a);
                    a = max(a, texture2D(map, vUv + (vec2(0.866, -0.500) * resolution * w)).a);
                    a = max(a, texture2D(map, vUv + (vec2(0.707, -0.707) * resolution * w)).a);
                    a = max(a, texture2D(map, vUv + (vec2(0.500, -0.866) * resolution * w)).a);
                    a = max(a, texture2D(map, vUv + (vec2(0.259, -0.966) * resolution * w)).a);
                    a = max(a, texture2D(map, vUv + (vec2(0.000, -1.000) * resolution * w)).a);
                    a = max(a, texture2D(map, vUv + (vec2(-0.259, -0.966) * resolution * w)).a);
                    a = max(a, texture2D(map, vUv + (vec2(-0.500, -0.866) * resolution * w)).a);
                    a = max(a, texture2D(map, vUv + (vec2(-0.707, -0.707) * resolution * w)).a);
                    a = max(a, texture2D(map, vUv + (vec2(-0.866, -0.500) * resolution * w)).a);
                    a = max(a, texture2D(map, vUv + (vec2(-0.966, -0.259) * resolution * w)).a);
                    a = max(a, texture2D(map, vUv + (vec2(-1.000, -0.000) * resolution * w)).a);
                    a = max(a, texture2D(map, vUv + (vec2(-0.966, 0.259) * resolution * w)).a);
                    a = max(a, texture2D(map, vUv + (vec2(-0.866, 0.500) * resolution * w)).a);
                    a = max(a, texture2D(map, vUv + (vec2(-0.707, 0.707) * resolution * w)).a);
                    a = max(a, texture2D(map, vUv + (vec2(-0.500, 0.866) * resolution * w)).a);
                    a = max(a, texture2D(map, vUv + (vec2(-0.259, 0.966) * resolution * w)).a);
                    return a > 0.1 ? 4.0 / w : 0.0;
                }
                // 卷积计算 周围有像素点的地方 距离自己越近权重越高
                float A() {
                    float a = 0.0;
                
                    for(float i = 1.0; i < 4.0; i++) {
                        a += distance(i) * (4.0 - i);
                    }
                    return a / 4.0;
                }
                void main() {
                    if(distance(5.0) > 0.1) { // 非模型附近的像素点就不计算了, 节约性能
                        if(texture2D(map, vUv).a < 0.1) {
                            // 描边的透明度
                            float strokeA = A();
                            gl_FragColor = vec4(0.0156, 0.980, 0.070, strokeA);
                        } else {
                
                            // 背景网格的透明度
                            float backgroundA = texture2D(background, (vUv * 15.0 * vec2(1.0, resolution.x / resolution.y) + offset)).a;
                            
                            if(backgroundA > 0.1) {
                                gl_FragColor = vec4(0.0156, 0.980, 0.070, backgroundA * 3.0);
                            } else {
                                gl_FragColor = vec4(0.0156, 0.980, 0.070, 0.05);
                            }

                        }
                    }
                }
            `,
            side: 2,
            depthTest: false,
            alphaTest: 0.001,
            transparent: true
        })

    );// 轮廓模型



}

const reactThreeDevTool = new ReactThreeDevTool();

export default reactThreeDevTool;